
package com.lbs.core.shared.tables;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import com.lbs.core.shared.parsing.LbsParser;
import com.lbs.core.shared.parsing.LbsParserException;

/**
 *Clase principal para la comunicacion de datos entre el servidor y la parte cliente de LizBet.
 * 
 * 04/Noviembre/2011
 * 
 * <p>
 * Cualquier tipo de datos de la base de datos biaja como una lista de objetos de esta clase, contiene el Key de cada elemento
 * y sus campos en forma de lista de LbsKeyValuePair
 * </p>
 * 
 * @author Alberto Sandoval Sotelo
 * 
 */

public class AbstractData implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 8073403114444161845L;
	private List<LbsKeyValuePair> data = new ArrayList<LbsKeyValuePair>();
	private String key;
	private String entityName;
    
    public AbstractData(String key){
    	this.setKey(key);
    }
    public AbstractData(){
    }
    
    
    /**
     * Llena los datos del AbstractData a partir de una cadena de texto formateada como AbstractData
     * @param data
     */
    public void parse(String data){
    	String pairs[] = data.split("--");
    	
    	for (String pair : pairs){
    		String p[] = pair.split("::");
    		if (p.length>1)
    			add(p[0],p[1]);
    	}
    }
    
    /**
     * Regresa el valor del campo espesificado por field
     * 
     * 
     * @param field Nombre del campo (o ruta) del cual se quiere su valor
     * @return
     */
    public Object get(String field){
//    	System.out.println(field);
    	int pointPos = field.indexOf(".");
    	if (pointPos!=-1)
    		return getOf(getAbstractData(field.substring(0, pointPos)), field.substring(pointPos+1));
    	else{
	    	for (LbsKeyValuePair d : data)
	    		if (d.getKey().equals(field))
	    			return d.getValue();
	    	//Si llegamos hasta aqui, no se encontro el key
	    	return null;
    	}
    }
    
    /**
     * Elimina un elemento del set
     * 
     * 
     * @param field Nombre del campo (o ruta) del cual se quiere su valor
     * @return
     */
    public void remove(String field){
    	for (LbsKeyValuePair d : data)
    		if (d.getKey().equals(field)){
    			data.remove(d);
    			break;
    		}
    }
    
    /**
     * Para ir sacando valores de alguna ruta
     * 
     * @param data
     * @param field
     * @return
     */
    public Object getOf(AbstractData data, String field){
    	if (data==null)
    		return null;
    	int pointPos = field.indexOf(".");
    	if (pointPos!=-1)
    		return getOf(data.getAbstractData(field.substring(0, pointPos)), field.substring(pointPos+1));
    	else
    		return data.get(field);
    }
    
    /**
     * Regresa el valor del campo espesificado por field, como tipo Double
     * 
     * 
     * @param field Nombre del campo del cual se quiere su valor
     * @return
     */
    public Double getDouble(String field){
    	if (get(field)==null)
    		return null;
    	else
    		return new Double(get(field).toString());
    }
    /**
     * Regresa el valor del campo espesificado por field, como tipo AbstractData
     * 
     * 
     * @param field Nombre del campo del cual se quiere su valor
     * @return
     */
    public AbstractData getAbstractData(String field){
    	return (AbstractData)get(field);
    }
    /**
     * Regresa el valor del campo espesificado por field, como tipo Integer
     * 
     * 
     * @param field Nombre del campo del cual se quiere su valor
     * @return
     */
    public Integer getInteger(String field){
    	return new Integer(get(field).toString());
    }
    /**
     * Regresa el valor del campo espesificado por field, como tipo Date
     * 
     * 
     * @param field Nombre del campo del cual se quiere su valor
     * @return
     */
    public Date getDate(String field){
    	return (Date)get(field);
    }
    /**
     * Regresa el valor del campo espesificado por field, como tipo Date
     * 
     * 
     * @param field Nombre del campo del cual se quiere su valor
     * @return
     */
    public String getString(String field){
        if (LbsParser.test(field).booleanValue()) {
            try {
              if (LbsParser.parse(field, null, this) == null) {
                return "";
              }
              return LbsParser.parse(field, null, this).toString();
            }
            catch (LbsParserException e) {
              e.printStackTrace();
              return null;
            }
          }
          return (String)get(field);
    }
    
    /**
     * Agrega un campo al arreglo.
     * 
     * @param key Nombre del campo o identificador
     * @param val Valor
     */
	public void add(String key, Object val){
		for (LbsKeyValuePair kv : data)
			if (kv.getKey().equals(key)){
				kv.setValue(val);
				return;
			}
		data.add(new LbsKeyValuePair(key,val));
	}
	
    @Override
    public int hashCode() {
        int hash = 0;
        hash += (key != null ? key.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof AbstractData)) {
            return false;
        }
        AbstractData other = (AbstractData) object;
        if ((this.key == null && other.key != null) || (this.key != null && !this.key.equals(other.key))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "[" + key + ": " + data + "]";
    }

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}
	public List<LbsKeyValuePair> getData() {
		return data;
	}
	public String getEntityName() {
		return entityName;
	}
	public void setEntityName(String entityName) {
		this.entityName = entityName;
	}
	
	/**
	 * Carga al objeto actual los datos del objeto newData
	 * 
	 * 
	 * @param newData Objecto del que se copiaran todos los datos
	 */
	public void update(AbstractData newData){
		this.data = newData.data;
		this.entityName = newData.entityName;
		this.key = newData.key;
	}
	
	/**
	 * Agrega al objeto actual los datos del objeto newData
	 * 
	 * 
	 * @param newData Objecto del que agregaran todos los datos
	 */
	public void appEnd(AbstractData newData){
		this.data.addAll(newData.data) ;
	}
	
	/**
	 * Convierte los datos en una cadena URL del tipo: ?dato1=valor&dato2=valor&dato3=valor
	 * @return
	 */
	public String toUrl(){
		String result="?";
		List<String> values = new ArrayList<String>();
		//Extraemos las parejas de datos
		for (LbsKeyValuePair p : data)
			if (p.getValue() instanceof AbstractData)
				values.add(p.getKey()+"="+((AbstractData)p.getValue()).getKey());
			else
				values.add(p.getKey()+"="+p.getValue().toString());
		//Coonstruimos la cadena
		for (String p : values)
			result+=p+"&";
		//Quitamos el ultimo &
		result=result.substring(0, result.length()-1);
		return result;
	}
	
	/**
	 * Verifica si el objeto AbstractData contiene un key con el nombre pasado como parametro
	 * @param key Nombre del key buscado
	 * @return true o false
	 */
	public Boolean hasKey(String key){
    	for (LbsKeyValuePair d : data)
    		if (d.getKey().equals(key))
    			return true;
    	return false;
	}
}



